/* ***********************************************************
	ButterflyAlarmClock.c
	Joe Pardue December 31, 2009
	Revision: 1.00

	License: 
	This software is not suitable for any purpose whatsoever.
	The moment it is activated, it will destroy any machine
	it is run on and kill all life in a one kilometer radius. 
	Have a nice day!

	Credits:
	Some of this was derived or maybe lifted directly from:
	1. The original Butterfly source code
	2. Martin Thomas' port of that code from IAR to GCC
	3. Dean Camera's various efforts
	4. Jim Remington's additions to Dean's LCD code
	5. And other's whose names I forgot in the stampede.
*********************************************************** */

#include "smws7.h"
#include "ButterflyAlarmClock.h"

int main(void)
{
	uint8_t test=0;
	uint8_t key = 0;
	int i;

	// Initialize the smws7 functions
	smws7Init();
	rtcInit();	// Initialize the real time clock

	// Set the real time clock
	lcdPutString("SET CLOCK");
	delay(5000); // Display for 5 seconds
	setClockToZero();
	setRTC();

	while (1) // forever
	{
		// This waits for 1 second to update the clock
		if(key == 0)
		{
			// Check for Alarm
			if(isAlarm == FALSE)
			{
				for(i = 0 ; i < 6 ; i++)
				{
					if(RTC_buffer[i] != Alarm_buffer[i]) break;

				}
				if(i == 5)
				{ 
					isAlarm = TRUE;
					alarm();
				}
			}

			while (RTC_buffer[5]==test);  //hang until seconds digit changes
		    showClock();
			test=RTC_buffer[5];
		}

		key = getkey();

		if(key)
		{
			isAlarm = FALSE; // Any key shuts off alarm
			parseKey(key);
		}

	}
   return 0;
}

// Process the joystick key
void parseKey(uint8_t key)
{

	switch (key)
	{
		case KEY_NULL:
		{
			// We shouldn't see this
			lcdPutString("NULL");
			break;
		}
		case KEY_ENTER:
		{
			if(isAlarm) isAlarm = FALSE;
			break;
		}
		case KEY_NEXT:
		{
			if(isAlarm) isAlarm = FALSE;
			lcdPutString("SET CLOCK");
			delay(5000);
			setRTC();
			break;
		}
		case KEY_PREV:
		{
			if(isAlarm) isAlarm = FALSE;
			lcdPutString("SHOW CLOCK");
			delay(5000);
			showClock();
			break;
		}
		case KEY_PLUS :
		{
			if(isAlarm) isAlarm = FALSE;
			lcdPutString("SHOW ALARM");
			delay(5000);
			showAlarm();
			delay(5000);
			lcdPutString("SHOW CLOCK");
			delay(5000);
			showClock();
			break;
		}
		case KEY_MINUS:
		{
			if(isAlarm) isAlarm = FALSE;
			lcdPutString("SET ALARM");
			delay(5000);
			setAlarm();
			lcdPutString("ALARM SET");
			delay(5000);
			break;
		}
		default :
		{
			// We shouldn't see this
			lcdPutString("WHAT");
			break;
		}
	}
}

// While the isAlarm boolean is true, run the beeper once per second
void alarm()
{
	// Show that the alarm is working
	lcdPutString("RINGIE DINGIE");

	while(isAlarm)
	{
		beeper();			// Beep about 1kHz for two seconds
		delay(1000);
		//_delay_ms(1000);	// Pause for about 1 second
	}
}

// This is a crude way to make the peizo element beep at about 1kHz
// for about 2 seconds.
void beeper()
{
	int i = 0;

	for( i = 0 ; i < 1000 ; i ++ )
	{
		PORTB |= (1<<PB5);	// Turn the peizo on
		_delay_ms(1);		// Pause 1 millisecond
		PORTB &= ~(1<<PB5);	// Turn the peizo off
		_delay_ms(1);		// Pause 1 millisecond
		if(getkey()){ isAlarm = FALSE; break;} // Quit on any key			
	}
}

void setRTC(void)
{
	uint8_t RTC_max[]="125959";  //max values of each digit. Does not check for illegal entries
	uint8_t key;
	uint8_t digit=5; 		//adjust digits of BCD clock buffer according to user input

	showClock();

    TIMSK2 &= ~(1<<TOIE2);	// disable Timer2 overflow interrupt (stop the RTC)

	while (1) 
	{   
		key=getkey();

		if (key == KEY_PLUS)		//increment selected digit, with rollover
			{  
			RTC_buffer[digit]++;
			if (RTC_buffer[digit]>RTC_max[digit]) RTC_buffer[digit]='0';
			showClock();
			}
		
		if (key == KEY_MINUS)		//decrement selected digit, with rollover
		 	{ 
			RTC_buffer[digit]--;
			if (RTC_buffer[digit]<'0') RTC_buffer[digit]=RTC_max[digit];
			showClock();
			}

		if (key == KEY_NEXT)		//select digit to right
			{
			digit++;
			if (digit>5) digit=0;
			showClock();
			}

		if (key == KEY_PREV)		//select digit to left
			{
			digit--;
			if (digit>5) digit=5;  //wraparound for any invalid value
			showClock();
			}

		if (key == KEY_ENTER) break;		//all done, start clock and exit

	}

    TIMSK2 |= (1<<TOIE2);	// enable Timer2 overflow interrupt

}


void setAlarm(void)
{
	uint8_t RTC_max[]="125959";  //max values of each digit. Does not check for illegal entries
	uint8_t key;
	uint8_t digit=5; 		//adjust digits of BCD clock buffer according to user input

	showAlarm();

    TIMSK2 &= ~(1<<TOIE2);	// disable Timer2 overflow interrupt (stop the RTC)

	while (1) 
	{   
		key=getkey();

		if (key == KEY_PLUS)		//increment selected digit, with rollover
			{  
			Alarm_buffer[digit]++;
			if (Alarm_buffer[digit]>RTC_max[digit]) Alarm_buffer[digit]='0';
			showAlarm();
			}
		
		if (key == KEY_MINUS)		//decrement selected digit, with rollover
		 	{ 
			Alarm_buffer[digit]--;
			if (Alarm_buffer[digit]<'0') Alarm_buffer[digit]=RTC_max[digit];
			showAlarm();
			}

		if (key == KEY_NEXT)		//select digit to right
			{
			digit++;
			if (digit>5) digit=0;
			showAlarm();
			}

		if (key == KEY_PREV)		//select digit to left
			{
			digit--;
			if (digit>5) digit=5;  //wraparound for any invalid value
			showAlarm();
			}

		if (key == KEY_ENTER) break;		//all done, start clock and exit

	}

    TIMSK2 |= (1<<TOIE2);	// enable Timer2 overflow interrupt
}

// Alias the _delay_ms
void delay(unsigned int millisec)
{
    while (millisec--)
	{
        _delay_ms(1);
		if(getkey())
		{ 	// Quit alarm on any key
			isAlarm = FALSE;
			millisec = 0;
		}
	}			

}

// Initialize the real time clock
void rtcInit(void)
{
    delay(1000);            // After power on, wait 1 second for the watch crystal to stablize
    cli();					// Disable global interrupts
    TIMSK2 &= ~(1<<TOIE2);	// Disable Timer 2 interrupts
    ASSR = (1<<AS2);		// Select asynchronous operation of Timer2
    TCNT2 = 0;              // Clear TCNT2A 
    TCCR2A |= (1<<CS22) | (1<<CS20);		// Select precaler: 32.768 kHz / 128 = 1 sec between each overflow
    while((ASSR & 0x01) | (ASSR & 0x04));	// Wait for TCN2UB and TCR2UB to be cleared
    TIFR2 = 0xFF;			// Clear timer2 interrupt-flags
    TIMSK2 |= (1<<TOIE2);	// Enable Timer2 overflow interrupt
    sei();					// Enable global interrupt

	setClockToZero();
	/*int i;
	for(i = 0; i < 6 ; i++)
	{
		RTC_buffer[i] = '0';	// Initialize buffers with character '0'
		Alarm_buffer[i] = '0';
	}
	RTC_buffer[6] = 0;	// Terminate buffers with zero
	Alarm_buffer[6] = 0;
	*/
}

void setClockToZero()
{
	int i;
	for(i = 0; i < 6 ; i++)
	{
		RTC_buffer[i] = '0';	// Initialize buffers with character '0'
		Alarm_buffer[i] = '0';
	}
	RTC_buffer[6] = 0;	// Terminate buffers with zero
	Alarm_buffer[6] = 0;
}

// Show the clock on the LCD
void showClock(void)
{

	lcdPutString(RTC_buffer);
    lcdShowColons(true);
}

// Show the alarm setting on the LCD
void showAlarm(void)
{

	lcdPutString(Alarm_buffer);
    lcdShowColons(true);
}


// Timer2 interrupt service routine keeps track of time
ISR (TIMER2_OVF_vect)
{
	uint8_t i;
	uint8_t t[6];
	
	// Make a copy, so don't change digit more than once on rollover
	for (i=0;i<6;i++) t[i]=RTC_buffer[i];	

    t[5]++;		// Increment second

    if (t[5] > '9')
	{
		t[5]='0';	// Increment ten seconds
		t[4]++;
	 	if ( t[4] > '5')
		{
			t[4]='0';
			t[3]++;			// Increment minutes
    		if (t[3] > '9')
    		{
				t[3]='0';
				t[2]++;		// Increment ten minutes
        
		 		if (t[2] > '5')
				{
					t[2]='0';
					t[1]++;			// Increment hours
 					uint8_t b = t[0]; // Tens of hours, handle rollover at 19 or 23
	     			if ( ((b < '2') && (t[1] > '9')) || ((b=='2') && (t[1] > '3')) )
		 		   	{
						t[1]='0';
						t[0]++;		// Increment ten hours
						if (t[0] > '2') t[0]='0';
    				}
				}
			}
		}
	}
	for (i=0;i<6;i++) RTC_buffer[i]=t[i];  // Copy updated time
}
